package com.ccx.demo.open.auth.controller;

import com.ccx.demo.business.user.entity.TabUser;
import com.ccx.demo.business.user.service.UserService;
import com.ccx.demo.business.user.vo.TabUserVO;
import com.ccx.demo.config.init.AppConfig;
import com.ccx.demo.enums.RedisKey;
import com.ccx.demo.enums.TokenClient;
import com.ccx.demo.open.auth.dto.UsernamePasswordLoginDTO;
import com.ccx.demo.open.auth.service.AuthService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import com.support.annotations.AppSwaggerGroup;
import com.support.annotations.VueSwaggerGroup;
import com.support.annotations.WechatSwaggerGroup;
import com.support.mvc.entity.base.Result;
import com.support.mvc.enums.AppCode;
import com.utils.enums.Code;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 操作请求处理：授权
 *
 * @author 谢长春
 */
@Api(tags = "登录")
@ApiSort(1)
@RequestMapping("/open/auth")
@Controller
@Slf4j
@RequiredArgsConstructor
public class OpenAuthController {

    private final AppConfig appConfig;
    private final RedissonClient redissonClient;
    private final AuthService authService;
    private final UserService userService;
    private final ExecutorService multiThread;

//    /**
//     * 默认以 session 模式登录
//     */
//    @PostMapping("/login")
//    @ApiOperation(value = "登录", tags = {""})
//    @ApiOperationSupport(order = 1, author = "谢长春", ignoreParameters = {"body.method", "body.phone", "body.code"})
//    @ResponseBody
//    public Result<TabUserVO> login(
//            @RequestBody final UsernamePasswordLoginDTO body,
//            HttpServletRequest request,
//            HttpServletResponse response) {
//        final Result<TabUserVO> result = new Result<>();
//        try {
////            log.debug("request.getAuthType():"+request.getAuthType());
////            log.debug("request.getContextPath()"+request.getContextPath());
////            log.debug("request.getPathInfo()"+request.getPathInfo());
////            log.debug("request.getRequestURI()"+request.getRequestURI());
////            log.debug("request.getServletPath()"+request.getServletPath());
////            log.debug("request.getLocalAddr()"+request.getLocalAddr());
////            log.debug("request.getLocalName()"+request.getLocalName());
////            log.debug("request.getProtocol()"+request.getProtocol());
////            log.debug("request.getRemoteAddr()"+request.getRemoteAddr());
////            log.debug("request.getRemoteHost()"+request.getRemoteHost());
////            log.debug("request.getScheme()"+request.getScheme());
////            log.debug("request.getServerName()"+request.getServerName());
////            log.debug("request.getRemotePort()"+request.getRemotePort());
////            log.debug("request.getServerPort()"+request.getServerPort());
////            log.debug("request.getServletContext().getContextPath()"+request.getServletContext().getContextPath());
////            log.debug("request.getServletContext().getServerInfo()"+request.getServletContext().getServerInfo());
////            log.debug("request.getServletContext().getServletContextName()"+request.getServletContext().getServletContextName());
////            log.debug("request.getServletContext().getVirtualServerName()"+request.getServletContext().getVirtualServerName());
//            Code.A00004.assertNonNull(body, "登录参数不能为空");
//            switch (body.getMethod()) {
//                case SESSION:
//                    return loginBySession(body, request);
//                case TOKEN:
//                    return loginByToken(body, request, response);
//                case CODE:
//                    throw Code.A00002.toCodeException("暂不支持手机验证码登录模式");
//                default:
//                    break;
//            }
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//            result.setException(e);
//        }
//        return result;
//    }

//    /**
//     * 以 session 模式登录
//     */
//    @ApiIgnore
//    @PostMapping("/login")
//    @ResponseBody
//    public Result<TabUserVO> login(@RequestBody final UsernamePasswordLoginDTO body, HttpServletRequest request) {
//        return loginBySession(body, request);
//    }
//
//    /**
//     * 以 session 模式登录
//     */
//    @ApiOperation(value = "session 模式登录", tags = {""})
//    @PostMapping("/login/SESSION")
//    @ResponseBody
//    public Result<TabUserVO> loginBySession(@RequestBody final UsernamePasswordLoginDTO body, HttpServletRequest request) {
//        return new Result<TabUserVO>().execute(result -> {
//            Code.A00003.assertNonNull(body, "登录参数不能为空");
//            Code.A00001.assertNonBlank(body.getUsername(), "用户名不能为空");
//            Code.A00001.assertNonBlank(body.getPassword(), "密码不能为空");
//            // 登录成功之后，将用户信息放入session
//            final TabUser user = authService.login(body.getUsername(), body.getPassword(), request.getRemoteAddr());
//            final HttpSession session = request.getSession(true);
//            session.setAttribute(Session.user.name(), user);
//            result.setSuccess(user.toTabUserVO());
//        });
//    }

    /**
     * 以 token 模式登录
     */
    @AppSwaggerGroup
    @VueSwaggerGroup
    @WechatSwaggerGroup
    @ApiOperation(value = "1.token 模式登录", tags = {""}, notes =
            "client 对应的枚举值参考： /enums"
                    + "登录成功之后 x-token 写入本地缓存，发请求时 x-token 写入请求头"
                    + "，任意接口返回401表示token过期，需跳转登录页重新获取token"
                    + "，同一个账号在一个客户端只能有一个 token"
    )
    @PostMapping("/login/{client}")
    @ApiOperationSupport(order = 1, author = "谢长春", ignoreParameters = {"body.method", "body.phone", "body.code"})
    @ResponseBody
    public Result<TabUserVO> loginByToken(
            @ApiParam(required = true, value = "登录客户端", example = "PC") @PathVariable final TokenClient client,
            @RequestBody final UsernamePasswordLoginDTO body,
            HttpServletRequest request,
            HttpServletResponse response
    ) {
        return new Result<TabUserVO>().execute(result -> {
            Code.A00001.assertNonBlank(body.getUsername(), "用户名不能为空");
            Code.A00001.assertNonBlank(body.getPassword(), "密码不能为空");
            // 登录成功之后，将用户信息放入session； 同时生成 token 回传到前端
            TabUser user = authService.login(body.getUsername(), body.getPassword(), request.getRemoteAddr());
            // 生成token 放在响应头
            final String token = user.token(client);
            response.setHeader("x-token", token);
            result.setSuccess(user.toTabUserVO()).addExtras("x-token", token);
        });
    }

    /**
     * 发送手机验证码
     */
    @AppSwaggerGroup
    @VueSwaggerGroup
    @WechatSwaggerGroup
    @ApiOperation(value = "2.1发送手机验证码", tags = {""})
    @PostMapping("/sms-login-code/{phone}")
    @ApiOperationSupport(order = 2, author = "谢长春")
    @ResponseBody
    public Result<String> smsLoginCode(
            @ApiParam(required = true, value = "手机号", example = "18717942600") @PathVariable final String phone
    ) {
        return new Result<String>().execute(result -> {
            Code.A00002.assertNonBlank(phone, "手机号必填");
            // 4 位数字验证码
            final String code = RandomStringUtils.randomNumeric(4);
            final RMap<String, Object> loginPhoneCodeBucket = redissonClient.getMap(RedisKey.login_phone_code.key(phone));
            loginPhoneCodeBucket.put("code", code); // 缓存验证码
            loginPhoneCodeBucket.put("count", 0); // 计数，避免暴力破解，验证码输入错误次数达到 3 次时，删除验证码
            loginPhoneCodeBucket.expire(5, TimeUnit.MINUTES); // 验证码有效期 5 分钟
            if (appConfig.isVerifyPhoneCodeEnabled()) {
                multiThread.execute(() -> {
                    // fixme: 补充验证码发送逻辑
                });
                result.setSuccess("");
            } else { // 开发环境关闭验证码开关之后，直接返回验证码
                result.setSuccess(code);
            }
        });
    }


    /**
     * 手机验证码模式登录
     */
    @AppSwaggerGroup
    @VueSwaggerGroup
    @WechatSwaggerGroup
    @ApiOperation(value = "2.2手机验证码模式登录", tags = {""}, notes = "开发环境验证码随便输入")
    @PostMapping("/login/{client}/{phone}/{code}")
    @ApiOperationSupport(order = 3, author = "谢长春")
    @ResponseBody
    public Result<TabUserVO> loginByPhone(
            @ApiParam(required = true, value = "登录客户端", example = "PC") @PathVariable final TokenClient client,
            @ApiParam(required = true, value = "手机号", example = "18717942600") @PathVariable final String phone,
            @ApiParam(required = true, value = "验证码", example = "0000") @PathVariable final String code,
            HttpServletResponse response
    ) {
        return new Result<TabUserVO>().execute(result -> {
            Code.A00002.assertNonBlank(phone, "手机号必填");
            Code.A00002.assertNonBlank(code, "验证码必填");
            if (appConfig.isVerifyPhoneCodeEnabled()) {
                final RMap<String, Object> loginPhoneCodeBucket = redissonClient.getMap(RedisKey.login_phone_code.key(phone));
                Code.A00002.assertHasTrue(loginPhoneCodeBucket.isExists(), "验证码已过期");
                if (Objects.equals(code, loginPhoneCodeBucket.get("code"))) {
                    loginPhoneCodeBucket.delete(); // 验证码校验成功
                } else {
                    final int count = Optional
                            .ofNullable(loginPhoneCodeBucket.get("count"))
                            .map(Objects::toString)
                            .map(Integer::parseInt)
                            .orElse(0)
                            + 1;
                    loginPhoneCodeBucket.put("count", count);
                    if (count >= 3) { // 计数，避免暴力破解，验证码输入错误次数达到 3 次时，删除验证码
                        loginPhoneCodeBucket.delete();
                    }
                    throw Code.A00002.toCodeException("验证码错误");
                }
            }
            // 验证码验证成功之后，生成 token 回传到前端
            final TabUser user = userService.findUser(phone).orElseThrow(() -> AppCode.A01016.toCodeException("手机号未绑定用户"));
            // 生成token 放在响应头
            final String token = user.token(client);
            response.setHeader("x-token", token);
            result.setSuccess(user.toTabUserVO()).addExtras("x-token", token);
        });
    }

//    /**
//     * 激活账户
//     */
//    @GetMapping("/email/{uuid}")
//    public String activate(@PathVariable String uuid) {
//        return authService.activate(uuid);
//    }
//    /**
//     * 忘记密码，发送修改密码邮件
//     */
//    @PatchMapping("/forget/password")
//    @ResponseBody
//    public Result<Object> forgetPassword(@RequestBody(required = false) String body) {
//        Result<Object> result = new Result<>();
//        try {
//            authService.forgetPassword(JSON.parseObject(body).getString("email"));
//            result.setCode(SUCCESS);
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//            result.setCode(e);
//        }
//        return result;
//    }

}
